/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 object_registration.cpp

	$Header: /heroes4/object_registration.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "object_registration.h"

#include <vector>
#include "adv_object_model.h"
#include "adv_object_type.h"
#include "stationary_adventure_object.h"

// -------------------------------------------------------------------------------
// information about how to construct various adventure objects
// -------------------------------------------------------------------------------
namespace
{
	// ------------------------------------------------------------
	// function table for a major subtype
	// ------------------------------------------------------------
	class t_subtype_table
	{
	public:
		t_subtype_table();

		void                   add( t_object_factory_base const* function );
		void				   add( int type, t_object_factory_base const* function );
		t_object_factory_base const* get( t_qualified_adv_object_type const& type ) const;
	protected:
		t_object_factory_base const*              m_function;
		std::vector<t_object_factory_base const*> m_subtypes;
	};

	inline t_subtype_table::t_subtype_table()
	{
		m_function = 0;
	}

	inline void t_subtype_table::add( t_object_factory_base const* function )
	{
		m_function = function;
	}

	// ------------------------------------------------------------
	// function table for an adventure object type
	// ------------------------------------------------------------
	class t_function_entry
	{
	public:
		t_function_entry();

		void                  add( t_object_factory_base const* function );
		void				  add( int type, t_object_factory_base const* function );
		void				  add( int type, int subtype, t_object_factory_base const* function );
		t_object_factory_base const* get( t_qualified_adv_object_type const& type ) const;
	protected:
		t_object_factory_base const*        m_function;
		std::vector<t_subtype_table> m_subtypes;
	};

	class t_object_creator
	{
	public:
		void				  add( t_adv_object_type type, t_object_factory_base const* function );
		void				  add( t_adv_object_type type, int major_subtype, 
			                       t_object_factory_base const* function );
		void				  add( t_adv_object_type type, int major_subtype, int minor_subtype,
			                       t_object_factory_base const* function );

		t_stationary_adventure_object* create( std::string const& name,
											   t_qualified_adv_object_type const& type ) const;
	protected:
		t_function_entry m_create_function[k_adv_object_type_count];
	};
}; // unnamed namespace


// -------------------------------------------------------------------------------
// information about how to construct various adventure objects
// -------------------------------------------------------------------------------
static t_object_factory<t_stationary_adventure_object> const k_create_stationary_object;

// ------------------------------------------------------------
// function table for a major subtype
// ------------------------------------------------------------
void t_subtype_table::add( int type, t_object_factory_base const* function )
{
	if (type >= m_subtypes.size())
		m_subtypes.insert( m_subtypes.end(), m_subtypes.size() - type + 1, 0 );
	m_subtypes[type] = function;
}

// ------------------------------------------------------------
// function table for a major subtype
// ------------------------------------------------------------
t_object_factory_base const* t_subtype_table::get( t_qualified_adv_object_type const& type ) const
{
	int subtype = type.minor_subtype();

	if (subtype >= m_subtypes.size())
		return m_function;
	if (m_subtypes[subtype] == 0)
		return m_function;
	return m_subtypes[subtype];
}

// ------------------------------------------------------------
// function table for an adventure object type
// ------------------------------------------------------------
t_function_entry::t_function_entry()
{
	m_function = &k_create_stationary_object;
}

// ------------------------------------------------------------
// function table for an adventure object type
// ------------------------------------------------------------
void t_function_entry::add(t_object_factory_base const* function )
{
	m_function = function;
}

// ------------------------------------------------------------
// function table for an adventure object type
// ------------------------------------------------------------
void t_function_entry::add( int type, t_object_factory_base const* function )
{
	if (type >= m_subtypes.size())
		m_subtypes.insert( m_subtypes.end(), type - m_subtypes.size() + 1, t_subtype_table() );
	m_subtypes[type].add( function );
}

// ------------------------------------------------------------
// function table for an adventure object type
// ------------------------------------------------------------
void t_function_entry::add( int type, int subtype, t_object_factory_base const* function )
{
	if (type >= m_subtypes.size())
		m_subtypes.insert( m_subtypes.end(), type - m_subtypes.size() + 1, t_subtype_table() );
	m_subtypes[type].add( subtype, function );
}

// ------------------------------------------------------------
// function table for an adventure object type
// ------------------------------------------------------------
t_object_factory_base const* t_function_entry::get( t_qualified_adv_object_type const& type ) const
{
	int subtype = type.major_subtype();

	if (subtype >= m_subtypes.size())
		return m_function;
	
	t_object_factory_base const* result = m_subtypes[subtype].get( type );

	if (result == 0)
		return m_function;
	return result;
}

// -------------------------------------------------------------------------------
// create a object table
// -------------------------------------------------------------------------------
t_stationary_adventure_object* t_object_creator::create( std::string const& name,
														 t_qualified_adv_object_type const& type ) const
{
	return m_create_function[type.get()].get( type )->create( name, type );
}

// -------------------------------------------------------------------------------
// create a object table
// -------------------------------------------------------------------------------
void t_object_creator::add( t_adv_object_type type, t_object_factory_base const* function )
{
	m_create_function[type].add( function );
}

// -------------------------------------------------------------------------------
// create a object table
// -------------------------------------------------------------------------------
void t_object_creator::add( t_adv_object_type type, int major_subtype, 
			                       t_object_factory_base const* function )
{
	m_create_function[type].add( major_subtype, function );
}

// -------------------------------------------------------------------------------
// create a object table
// -------------------------------------------------------------------------------
void t_object_creator::add( t_adv_object_type type, int major_subtype, int minor_subtype,
			                       t_object_factory_base const* function )
{
	m_create_function[type].add( major_subtype, minor_subtype, function );
}

// construct object inside function to insure it is not initialized until all functions
// are registered.
static t_object_creator& get_object_creator()
{
	static t_object_creator object_creator;

	return object_creator;
}

// -------------------------------------------------------------------
// object which records function for creating an object
// -------------------------------------------------------------------
t_object_registration_base::t_object_registration_base( t_adv_object_type type, 
													    t_object_factory_base const* func )
{
	t_object_creator& creator = get_object_creator();

	creator.add( type, func );
};

// -------------------------------------------------------------------
// object which records function for creating an object
// -------------------------------------------------------------------
t_object_registration_base::t_object_registration_base( t_adv_object_type type, int major_subtype,
											  t_object_factory_base const* func )
{
	t_object_creator& creator = get_object_creator();

	creator.add( type, major_subtype, func );
};

// -------------------------------------------------------------------
// object which records function for creating an object
// -------------------------------------------------------------------
t_object_registration_base::t_object_registration_base( t_adv_object_type type, int major_subtype,
											  int minor_subtype, t_object_factory_base const* func )
{
	t_object_creator& creator = get_object_creator();

	creator.add( type, major_subtype, minor_subtype, func );
};

// -------------------------------------------------------------------
// create an object
// -------------------------------------------------------------------
t_stationary_adventure_object* create_adv_object( std::string const& name )
{
	t_stationary_adventure_object source( name );

	t_adv_object_model const& model = source.get_model();
	t_qualified_adv_object_type const& type = model.get_qualified_type();


	return get_object_creator().create( name, type );
}

// -------------------------------------------------------------------
// create an object
// -------------------------------------------------------------------
t_stationary_adventure_object* create_adv_object( std::string const& name,
										       t_qualified_adv_object_type const&   type )
{
	return get_object_creator().create( name, type );
}
